package com.shyt.util;

import com.shyt.exception.CustomerAuthenticationException;
import com.shyt.security.AuthUserDetailUtil;
import com.shyt.security.JwtUtils;
import com.shyt.security.LoginFailUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
@Slf4j
public class CheckTokenFilterUtill extends OncePerRequestFilter {
    @Value("${request.login.url}")
    private String loginUrl;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    JwtUtils jwtUtils;

    @Autowired
    AuthUserDetailUtil authUserDetailUtil;

    @Autowired
    LoginFailUtil loginFailUtil;


    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        log.info("进入token认证,验证开始__________________________________");
        //第一步： 过滤非登录的请求，登录是去取，生成token，此时是无token的
        String requestURI = request.getRequestURI();
        if (!loginUrl.equals(requestURI)) {
            try {
                //header的token
                String headerToken = request.getHeader("token");
                //body的token
                String bodyToken = request.getParameter("token");
                //优先级，头部大于尾部
                String token = !StringUtils.isEmpty(headerToken) ? headerToken : bodyToken;
                if (StringUtils.isEmpty(token)) {
                    throw new CustomerAuthenticationException("登录信息信息不完善");
                    //待定，异常，token为空
                }

                //token是已经存在了，我们需要对token进行校验
                //redisTemplate.opsForValue().set("token_", token, -1);
                //String redisUsername = (String) redisTemplate.opsForValue().get("token_" + token);

                String redisUsername = String.valueOf(
                        redisTemplate.opsForHash().get("tokenSession", "token_" + token));

                //先取redis的名称，如果为空，则接口的访问登录token信息有误
                if (StringUtils.isEmpty(redisUsername) || "null".equals(redisUsername)) {
                    throw new CustomerAuthenticationException("登录信息异常，请重新登录");
                    //待定，异常，token为空
                }
                //token和redisToken相同，那我们要判断token的持有人是否一致
                //jwt工具的token
                String jwtUsername = jwtUtils.getUsernameFromToken(token);

                //再对redisusername和jwtusername进行比对，防止越权
                if (!redisUsername.equals(redisUsername)) {
                    throw new CustomerAuthenticationException("用户信息有误，请检查");//待定，异常，token为空
                }
                //再取登录信息里边的username,判断是否用户名相同，功能点待定
                UserDetails userDetails =
                        authUserDetailUtil.loadUserByUsername(jwtUsername);

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

                usernamePasswordAuthenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                log.info("进入token认证,验证结束__________________________________");
            } catch (CustomerAuthenticationException customerAuthenticationException) {
                loginFailUtil.onAuthenticationFailure(request, response, customerAuthenticationException);
            }
        }
        doFilter(request, response, filterChain);
    }
}
